﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cinchoo.Core.ETL;
using ChoWorkflowServer.Model;

namespace ChoWorkflowServer
{
    public class ChoWeeklyCalendarIntervalTrigger : ChoETLCalendarIntervalTrigger
    {
        private ChoTasksDataSet.CHO_TASK_SCHEDULESRow _scheduleRow;
        private bool _firstTime = true;
        private DateTimeOffset? _origAfterTime;
        private IEnumerator<DateTimeOffset?> _nextFireTimeUtcDates = null; // new List<DateTimeOffset?>();

        public ChoWeeklyCalendarIntervalTrigger(string name)
            : this(name, DateTimeOffset.UtcNow, null)
        {
        }

        /// <summary>
        /// Create a <see cref="ICalendarIntervalTrigger" /> that will occur at the given time,
        /// and repeat at the the given interval until the given end time.
        /// </summary>
        /// <param name="name">Name for the trigger instance.</param>
        /// <param name="startTimeUtc">A <see cref="DateTimeOffset" /> set to the time for the <see cref="ITrigger" /> to fire.</param>
        /// <param name="endTimeUtc">A <see cref="DateTimeOffset" /> set to the time for the <see cref="ITrigger" /> to quit repeat firing.</param>
        /// <param name="intervalUnit">The repeat interval unit (minutes, days, months, etc).</param>
        /// <param name="repeatInterval">The number of milliseconds to pause between the repeat firing.</param>
        public ChoWeeklyCalendarIntervalTrigger(string name, DateTimeOffset startTimeUtc,
                                           DateTimeOffset? endTimeUtc)
            : base(name, startTimeUtc, endTimeUtc, ChoETLIntervalUnit.Week, 1)
        {
        }
        
        public override DateTimeOffset? ComputeFirstFireTime(IChoETLCalendar calendar)
        {
            ComputeNextFireDates(base.ComputeFirstFireTime(calendar));

            DateTimeOffset? nextFireTime = null;
            if (_nextFireTimeUtcDates != null && _nextFireTimeUtcDates.MoveNext())
                nextFireTime = _nextFireTimeUtcDates.Current;

            return nextFireTime;
        }

        public override DateTimeOffset? GetFireTimeAfter(DateTimeOffset? afterTime)
        {
            if (_nextFireTimeUtcDates == null) return null;

            if (_firstTime)
            {
                _firstTime = false;
                _origAfterTime = afterTime;
            }

            DateTimeOffset? nextFireTime = null;
            if (!_nextFireTimeUtcDates.MoveNext())
            {
                int everyNoOfWeeks = _scheduleRow != null && !_scheduleRow.IsWEEKLY_EVERY_WEEKSNull() ? _scheduleRow.WEEKLY_EVERY_WEEKS : 1;
                if (everyNoOfWeeks <= 0)
                    throw new ApplicationException("Invalid EveryNoOfWeeks found.");

                for (int counter = 0; counter < everyNoOfWeeks; counter++)
                {
                    _origAfterTime = nextFireTime = base.GetFireTimeAfter(_origAfterTime);
                    if (nextFireTime == null)
                        break;
                }

                ComputeNextFireDates(nextFireTime);
            }
            else
                nextFireTime = _nextFireTimeUtcDates.Current;

            return nextFireTime;
        }

        private void ComputeNextFireDates(DateTimeOffset? nextFireTime)
        {
            if (nextFireTime == null)
                _nextFireTimeUtcDates = null;
            else
            {
                DateTimeOffset lNextFireDateTime = nextFireTime.Value.DateTime;
                for (int i = 0; i < 7; i++)
                {
                    DayOfWeek dayOfWeek = lNextFireDateTime.Date.DayOfWeek;

                    switch (dayOfWeek)
                    {
                        case DayOfWeek.Sunday:
                            if (_scheduleRow.SUN && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Monday:
                            if (_scheduleRow.MON && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Tuesday:
                            if (_scheduleRow.TUE && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Wednesday:
                            if (_scheduleRow.WED && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Thursday:
                            if (_scheduleRow.THU && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Friday:
                            if (_scheduleRow.FRI && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                        case DayOfWeek.Saturday:
                            if (_scheduleRow.SAT && (EndTimeUtc == null || lNextFireDateTime <= EndTimeUtc.Value))
                                _nextFireTimeUtcDates = ChoCalendarIntervalUtil.ComputeRepeatFireDates(_scheduleRow, lNextFireDateTime).GetEnumerator();
                            break;
                    }

                    lNextFireDateTime = lNextFireDateTime.AddDays(1);
                }
            }
        }

        public override object Tag
        {
            get
            {
                return _scheduleRow;
            }
            set
            {
                _scheduleRow = value as ChoTasksDataSet.CHO_TASK_SCHEDULESRow;
            }
        }
    }
}
